#include "Scene.h"


Scene::Scene(Player * p)
{
	player = p;

	field = new Field(28,31,13,13,14,11,17,16,10,12);
	characters = new vector <Character *>;

	createCharacters();

	items = new vector <Collectable *>;
	placeItems(1,1);
	removeSquareballs(0,14,5,14);
	removeSquareballs(7,11,20,19);
	removeSquareballs(22,14,27,14);
	transformBall(1,3);
	transformBall(1,23);
	transformBall(26,3);
	transformBall(26,23);


	zoom = 0.40;
	angleX = 30;
	angleY = 0;

	mouse = false;
	wheel = false;

	eaterCounter = -1;

	beginCounter = (double) clock()/CLOCKS_PER_SEC;
}


Scene::~Scene(void)
{
	for(int i = 0 ; i < (int)characters->size();i++)
	{
		delete characters->at(i);
	}
	delete characters;

	for(int i = 0 ; i < (int)items->size();i++)
	{
		delete items->at(i);
	}
	delete items;
	delete field;
}


void Scene :: displayAll1(){


	//gestion de la sourie
	if(mouse)
	{
      angleX =  angleX + (bufferMousePositionY - currentMousePositionY)/5;
		if(angleX > 90)
			angleX = 90;
		else if (angleX < 0)
			angleX = 0;
		bufferMousePositionY = currentMousePositionY;

		angleY =  angleY + (bufferMousePositionX - currentMousePositionX)/5;
        angleY = (angleY >= 360)?(angleY - 360):(angleY < 0)?(angleY = 360 + angleY):angleY;

		bufferMousePositionX = currentMousePositionX;

	}

   //gestion de la roulette
   if(wheel)
	{
      if (zoom<0)
         zoom = 0;
      else
         zoom =  zoom + (bufferMousePositionY - currentMousePositionY)/50;
		bufferMousePositionY = currentMousePositionY;
	}

	// definition de la position de base de la scene
	glPushMatrix();
	glRotatef(angleX,1,0,0);
	glRotatef(angleY,0,1,0);
	glScalef(zoom,zoom,zoom);
	glPushMatrix();

	//on affiche tout les composant de la scene
	field->display();

	for(unsigned i=0;i<items->size();i++)
	{
		items->at(i)->display();
	}


	glPushMatrix();
	for(unsigned i=0;i<characters->size();i++)
	{
		characters->at(i)->display();
	}
	glPopMatrix();
	glPopMatrix();
	glPopMatrix();

}

void Scene :: displayAll2(){
	// definition de la position de base de la scene
	glPushMatrix();

	glScalef(0.40,.40,.40);

	SetCameraPosition();

	//on affiche tout les composant de la scene
	field->display();

	for(unsigned i=0;i<items->size();i++)
	{
		items->at(i)->display();
	}


	for(unsigned i=1;i<characters->size();i++)
	{
		characters->at(i)->display();
	}

	//glPopMatrix();

	glPopMatrix();

}

void Scene :: SetCameraPosition()
{
	float milieuSceneX = (float)field->getWidth() /2;
	float milieuSceneZ = (float)field->getHeight() /2;
	float positionCameraX = 0;
	float positionCameraZ = 0;
	if(getPacman()->getX() < milieuSceneX){
		if( (int)(milieuSceneX*10) % 10 == 0){

			positionCameraX -= 0.5;
			milieuSceneX --;
		}
		positionCameraX -= milieuSceneX - getPacman()->getX();
	}
	else{
		if( (int)(milieuSceneX*10) % 10 == 0){
			positionCameraX += 0.5;
		}
		positionCameraX += getPacman()->getX() - milieuSceneX;
	}

	if(getPacman()->getZ() < milieuSceneZ){
		if( (int)(milieuSceneZ*10) % 10 == 0){
			positionCameraZ -= 0.5;
			milieuSceneZ --;
		}
		positionCameraZ -= milieuSceneZ - getPacman()->getZ()-0.5;
	}
	else{
		if( (int)(milieuSceneZ*10) % 10 == 0){
			positionCameraZ += 0.5;
		}
		positionCameraZ += getPacman()->getZ()+0.5 - milieuSceneZ;
	}

	//calcul d'angle
	float buffAngle = getPacman()->getAngle();
	while(buffAngle>85)
		buffAngle -=90;
	float ratio = (9 - buffAngle/10)/9;

	if(getPacman()->getAngle() > 265){
		gluLookAt(positionCameraX,0.5,positionCameraZ,
		positionCameraX + (1-ratio),0.5,positionCameraZ +ratio,
		0,1,0);
	}
	else if(getPacman()->getAngle() > 175){
		gluLookAt(positionCameraX,0.5,positionCameraZ,
		positionCameraX - ratio,0.5,positionCameraZ + (1-ratio),
		0,1,0);
	}
	else if(getPacman()->getAngle() > 85){
		gluLookAt(positionCameraX,0.5,positionCameraZ,
		positionCameraX - (1-ratio),0.5,positionCameraZ - ratio,
		0,1,0);
	}
	else{
		gluLookAt(positionCameraX,0.5,positionCameraZ,
		positionCameraX + ratio,0.5,positionCameraZ - (1-ratio),
		0,1,0);
	}
}

void Scene :: action(){

	if(((double)clock()/CLOCKS_PER_SEC) - beginCounter >= 3 )
		beginCounter = -1;

	if(!isGetReadyTime())
	{
		counterManagement();
		colisionManagement();

		for(unsigned i=0;i<characters->size();i++)
		{
			characters->at(i)->action();
		}
	}
}

void Scene :: colisionManagement(){
	if(getPacman()->isInTheMiddle())
	{
		int x=getPacman()->getAbsoluteX();
		int z=getPacman()->getAbsoluteZ();
		if(field->getGrid()->at(x)->at(z)==DESTRUCTOR)
		{
			eaterCounter =(double) clock()/CLOCKS_PER_SEC;
			for(unsigned i=1;i<characters->size();i++)
			{
				if( ((Ghost*)characters->at(i))->getComportement() == NORMAL)
					((Ghost*)characters->at(i))->setComportement(RUNAWAY);
			}
		}
		if(field->getGrid()->at(x)->at(z)==BALL || field->getGrid()->at(x)->at(z)==DESTRUCTOR)
		{
			player->increaseScore(10);

			field->getGrid()->at(x)->at(z)=VOID;
			int middle = items->size()/2;
			int borneMin = 0;
			int borneMax = items->size()-1;

			while(1)
			{
				bool jaaaa= !(items->at(middle)->getX() == getPacman()->getAbsoluteX() && items->at(middle)->getZ() == getPacman()->getAbsoluteZ());
				if(!jaaaa)
					break;

				if(items->at(middle)->getX() > getPacman()->getAbsoluteX())
				{
					borneMax = middle -1;
					middle = borneMin + (borneMax-borneMin)/2;
				}

				else if(items->at(middle)->getX() < getPacman()->getAbsoluteX())
				{
					borneMin = middle +1;
					middle = borneMin + (borneMax-borneMin)/2;
				}

				else
				{
					if(items->at(middle)->getZ() > getPacman()->getAbsoluteZ()){
						borneMax = middle -1;
						middle = borneMin + (borneMax-borneMin)/2;
					}
					else{
						borneMin = middle +1;
						middle = borneMin + (borneMax-borneMin)/2;
					}
				}
			}
			items->erase(items->begin()+middle);

		}
	}
}

void Scene :: counterManagement(){
	if(eaterCounter > 0)
	{
		if( ((double)clock()/CLOCKS_PER_SEC) - eaterCounter > 8)
			eaterCounter  = -1;
	}
	else
	{
		for(unsigned i=1;i<characters->size();i++)
			{
				if( ((Ghost*)characters->at(i))->getComportement() == RUNAWAY)
					((Ghost*)characters->at(i))->setComportement(NORMAL);
			}
	}
}

void Scene :: placeItems(int x, int z){
	fillItems(x,z);
	sortBalls();
}


void Scene :: fillItems(int x, int z){
	field->getGrid()->at(x)->at(z) = BALL;
	items->push_back(new Ball(x,z));

	if(x-1 >=0 && field->getGrid()->at(x-1)->at(z) == VOID)
		fillItems(x-1,z);
	if(x+1 != field->getWidth() && field->getGrid()->at(x+1)->at(z) == VOID)
		fillItems(x+1,z);
	if(z-1 >=0 && field->getGrid()->at(x)->at(z-1) == VOID)
		fillItems(x,z-1);
	if(z+1 != field->getHeight() && field->getGrid()->at(x)->at(z+1) == VOID)
		fillItems(x,z+1);
}

void Scene::sortBalls(){
	//tri des x
	for(unsigned i=0;i<items->size()-1;i++)
	{
		int j=i+1;
		while(j>0 && items->at(j)->getX() < items->at(j-1)->getX())
		{
			Collectable * temp = items->at(j);
			items->at(j) = items->at(j-1);
			items->at(j-1) = temp;
			j--;
		}
	}
	//tri des z
	int buff= items->at(0)->getZ();
	int borneMin = 0;
	int borneMax = 0;
	while(borneMax < (int)items->size())
	{
		if(items->at(borneMax)->getX() != items->at(borneMin)->getX())
		{
			for(int i=borneMin;i<borneMax-1;i++)
			{
				int j=i+1;
				while(j>borneMin && items->at(j)->getZ() < items->at(j-1)->getZ())
				{
					Collectable * temp = items->at(j);
					items->at(j) = items->at(j-1);
					items->at(j-1) = temp;
					j--;
				}
			}
			borneMin = borneMax;
		}
		else
		{
			borneMax++;
		}
	}
}

void Scene :: removeSquareballs(int x1, int z1, int x2, int z2){
	for (unsigned i=0;i<items->size();i++)
	{
		if( items->at(i)->getX() >= x1 && items->at(i)->getX() <= x2
			&& items->at(i)->getZ() >= z1 && items->at(i)->getZ() <= z2)
		{
			field->getGrid()->at(items->at(i)->getX() )->at(items->at(i)->getZ()) = VOID;
			delete items->at(i);
			items->erase(items->begin() + i);
			i--;
		}
	}
}

void Scene::transformBall(int x, int z){
	for(unsigned i=0; i< items->size() ; i++)
	{
		if(items->at(i)->getX() == x && items->at(i)->getZ() == z)
		{
			items->at(i) = new Destructor (x,z);
			field->getGrid()->at(x)->at(z) = DESTRUCTOR;
		}
	}
}

bool Scene :: colisionPacmanGhost(){

	for(unsigned i=1;i<characters->size();i++)
	{
		float d = sqrt( pow((getPacman()->getX()-characters->at(i)->getX()),2) +
			pow((getPacman()->getZ()-characters->at(i)->getZ()),2) );

		((Ghost *)characters->at(i))->setDistanceFromPacman(d);

		if((getPacman()->getWidth()/2 + characters->at(i)->getWidth()/2) >= d)
		{
			if(eaterCounter>0 && ((Ghost *)characters->at(i))->getComportement() != DEAD)
			{
				player->increaseScore(200);
				((Ghost *)characters->at(i))->setComportement(DEAD);
				return false;
			}
			else if(((Ghost*)characters->at(i))->getComportement() != DEAD)
				return true;
		}
	}

	return false;

}



void Scene :: createCharacters(){
	characters->push_back(new Pacman(14,23,270,1.5,field));
    characters->push_back(new Ghost(13,11,0,1,field,getPacman(),0));
	characters->push_back(new Ghost(13,14,0,1,field,getPacman(),1));
	characters->push_back(new Ghost(11,14,0,1,field,getPacman(),2));
	characters->push_back(new Ghost(16,14,0,1,field,getPacman(),3));

}

void Scene :: resetCharacters(){
	for(unsigned i =0;i<characters->size();i++)
	{
		delete characters->at(i);
	}
	characters->clear();
	createCharacters();
	beginCounter = ((double)clock()/CLOCKS_PER_SEC);
}

bool Scene :: win(){
	return items->size() == 0;
}

bool Scene :: lose(){
	return colisionPacmanGhost();
}

void Scene :: setZoom(float f){zoom = f;}
float Scene :: getZoom(){	return zoom;}
void Scene :: setAngleX(float f){	angleX = f;}
float Scene :: getAngleX(){	return angleX;}
void Scene :: setAngleY(float f){	angleY = f;}
float Scene :: getAngleY(){	return angleY;}
bool Scene :: getMouse(){	return mouse;}
void Scene :: setMouse(bool b){	mouse = b;}
void Scene :: setWheel(bool b){	wheel = b;}
bool Scene :: getWheel(){	return wheel;}
bool Scene :: isGetReadyTime(){return !(beginCounter==-1);}
float Scene :: getCurrentMousePositionX(){	return currentMousePositionX;}
float Scene ::getCurrentMousePositionY(){	return currentMousePositionX;}
float Scene :: getBufferMousePositionX(){	return bufferMousePositionX;}
float Scene ::getBufferMousePositionY(){	return bufferMousePositionY;}
void Scene ::setCurrentMousePositionX(float x){currentMousePositionX = x;}
void Scene ::setCurrentMousePositionY(float y){currentMousePositionY = y;}
void Scene ::setBufferMousePositionX(float x){bufferMousePositionX = x;}
void Scene ::setBufferMousePositionY(float y){bufferMousePositionY = y;}
Pacman * Scene :: getPacman(){return (Pacman *)characters->at(0);}
Field* Scene :: getField(){return field;}
